/**
 * initialize page
 *
 * expected option keys:
 * - selParents -- string selector for the selected parents element
 * - selRoles   -- string selector for the role list element
 * - roles      -- hash of roles, indexed by id and containing the label and parents
 *
 * @var jQuery $   reference to jQuery object
 * @var object app hash of options
 */
function init($, app) {
    // do stuff onDomReady
    $(function () {
        var parents = $(app.selParents)[0];
        var roles = $(app.selRoles)[0];
        var childrenOf = {}, ancestorsOf = {};

        /**
         * adds an option element where value = role and textNode = label
         *
         * @var string role
         * @var string label
         */
        function addOption(role, label) {
            if (-1 == this.opts.indexOf(role)) {
                this.opts.push(role);
                $('<option />').val(role).text(label).appendTo(this);
            }
        }

        /**
         * removes an option element where value == role
         *
         * @var string role
         */
        function removeOption(role) {
            var index = this.opts.indexOf(role);
            if (-1 != index) {
                this.opts.splice(this.opts.indexOf(role), 1);
                $('option[value=' + role + ']', this).remove();
            }
        }

        /**
         * recursively add child to ancestors' registry and the parents to
         * the child's ancestors
         *
         * could potentially loop endlessly if there is circular inheritance.
         * make sure there's no such thing in the passed roles hash
         *
         * @var string child
         * @var array  parents
         */
        function expandRole(child, parents) {
            for (var i in parents) {
                var p = parents[i];
                childrenOf[p] = childrenOf[p] || [];
                ancestorsOf[child] = ancestorsOf[child] || [];
                if (-1 == childrenOf[p].indexOf(child)) {
                    childrenOf[p].push(child);
                }
                if (-1 == ancestorsOf[child].indexOf(p)) {
                    ancestorsOf[child].push(p);
                }
                var grandParents = app.roles[p].parents;
                expandRole(child, grandParents);
            }
        }

        // initialize the two select elements
        $.each([parents, roles], function () {
            this.opts = [];
            this.addOption = addOption;
            this.removeOption = removeOption;
        });

        // populate role list
        for (var id in app.roles) {
            var role = app.roles[id];
            $('<option />').val(id).text(role.label).appendTo(roles);
            roles.opts.push(id);
            expandRole(id, role.parents);
        }

        // transfer selected option from role list to the selected list
        // gray out all the ancestors of the selected role
        $(app.selRoles + ' > option').live('click', function () {
            var id = this.value;
            var label = this.childNodes[0].textContent;
            parents.addOption(id, label);
            roles.removeOption(id);
            if (ancestorsOf[id]) {
                var ancestors = ancestorsOf[id];
                $('option', roles).each(function () {
                    var $this = $(this);
                    if (-1 != ancestors.indexOf($this.val())) {
                        $this.css('color', '#aaa');
                    }
                });
            }
        });

        // transfer selected option from selected to role list
        // un-gray the complement of the intersection of the ancestors of the
        // selected role and the ancestors of the rest of the selected roles
        $(app.selParents + ' > option').live('click', function () {
            var id = this.value;
            var label = this.childNodes[0].textContent;
            parents.removeOption(id);
            roles.addOption(id, label);
            var ancestors = ancestorsOf[id];
            var shouldBeGray = [];
            shouldBeGray = $.map(parents.opts, function (e) {
                var a = ancestorsOf[e] || [];
                return $.map(a, function (el) {
                    if (-1 == shouldBeGray.indexOf(el)) {
                        shouldBeGray.push(el);
                        return el;
                    }
                    return null;
                });
            });
            $('option', roles).each(function () {
                var $this = $(this);
                var val = $this.val();
                if (-1 != shouldBeGray.indexOf(val)) {
                    $this.css('color', '#aaa');
                } else if (ancestors && -1 != ancestors.indexOf(val)) {
                    $this.css('color', '');
                }
            });
        });

    });

}
